package cn.edu.sgu.www.mhxysy.shiro;

import cn.edu.sgu.www.mhxysy.consts.RedisKeyPrefixes;
import cn.edu.sgu.www.mhxysy.entity.system.Email;
import cn.edu.sgu.www.mhxysy.entity.system.User;
import cn.edu.sgu.www.mhxysy.enums.LockStatus;
import cn.edu.sgu.www.mhxysy.feign.clients.pms.PmsFeignService;
import cn.edu.sgu.www.mhxysy.redis.RedisUtils;
import cn.edu.sgu.www.mhxysy.restful.JsonResult;
import cn.edu.sgu.www.mhxysy.util.EmailUtils;
import cn.edu.sgu.www.mhxysy.util.PasswordEncoder;
import cn.edu.sgu.www.mhxysy.util.TimerUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.mail.MessagingException;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

/**
 * 密码匹配器
 * @author 沐雨橙风ιε
 * @version 1.0
 */
@Slf4j
@Component
public class UserPasswordMatcher implements CredentialsMatcher {

    private final EmailUtils emailUtils;
    private final RedisUtils redisUtils;
    private final PmsFeignService feignService;

    @Autowired
    public UserPasswordMatcher(
            EmailUtils emailUtils,
            RedisUtils redisUtils,
            PmsFeignService feignService) {
        this.emailUtils = emailUtils;
        this.redisUtils = redisUtils;
        this.feignService = feignService;
    }

    @Override
    public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        PrincipalCollection principals = authenticationInfo.getPrincipals();
        User user = (User) principals.getPrimaryPrincipal();

        // 账号被锁定，禁止登录
        if (LockStatus.isLocked(user.getLockStatus())) {
            throw new AuthenticationException("账号已被锁定，禁止登录！");
        }

        // 得到用户输入的密码
        String password = new String(token.getPassword());
        // 得到数据库密码
        String encodedPassword = user.getPassword();

        // 匹配密码
        boolean matches = PasswordEncoder.matches(password, encodedPassword);

        if (!matches) {
            // 得到用户名
            String username = user.getUsername();
            // 密码错误，防止暴力破解
            String key = RedisKeyPrefixes.PREFIX_USER_LOGIN_TIMES + username;

            if (!redisUtils.hasKey(key)) {
                redisUtils.set(key, "0");
                redisUtils.expire(key, 30, TimeUnit.MINUTES);
            }

            // 30分钟内登录次数大于5，直接停用账号
            Long loginTimes = redisUtils.incrBy(key);

            if (loginTimes >= 5) {
                String userId = user.getId();

                log.debug("账号{}在30分钟内登录失败5次，已经被锁定！", username);

                try {
                    // 锁定用户
                    JsonResult<Void> jsonResult = feignService.lockUser(userId);

                    if (jsonResult.isSuccess()) {
                        // 触发停用账号后删除key
                        redisUtils.delete(key);

                        // 发送邮件提醒
                        TimerUtils.schedule(new TimerTask() {
                            @Override
                            public void run() {
                                String message = "您的账号" + username + "30分钟内登录失败次数达到5次，已经被锁定！";

                                // 查询用户的邮箱
                                Email email = emailUtils.getEnableEmail(userId);

                                try {
                                    emailUtils.sendMessage(message, email.getAccount());
                                } catch (MessagingException e) {
                                    e.printStackTrace();
                                }
                            }
                        });
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            throw new AuthenticationException("登录失败，用户名或密码不正确~");
        }

        return true;
    }

}